import { _decorator } from "cc";
import { Logger } from "../common/log/Logger";
import { injectable } from "../inject";
const { ccclass, property } = _decorator;

interface EventData {
    event: string;
    funcName: string;
    once: boolean;
}

interface Listener {
    target: Object;
    cb: (...args: unknown[]) => void;
    once: boolean;
    priority: number;
}

@injectable('EventDispatcher')
export class EventDispatcher {

    private _eventsMap: Map<string, Map<Object, Listener[]>> = new Map();
    private _classMap: Map<Function, EventData[]> = new Map();

    public on(event: string, cb: (...args: any[]) => void, target: Object, once: boolean = false, priority: number = 0): void {
        if (!cb || !target) {
            Logger.error(`event: ${event} listener target 不能空`);
            return;
        }

        let map: Map<Object, Listener[]> = this._eventsMap.get(event);
        let list: Listener[] = [];

        if (map == undefined) {
            map = new Map();
            this._eventsMap.set(event, map);
        }

        let insertIndex = -1;

        list = map.get(target);
        if (list == undefined) {
            list = [];
            map.set(target, list);
        } else {
            let length = list.length;
            for (let i = 0; i < length; i++) {
                let bin = list[i];
                if (bin.cb === cb && bin.target === target) {
                    Logger.error(`event: ${event} 重复注册`);
                    return;
                }

                if (insertIndex === -1 && bin.priority < priority) {
                    insertIndex = i;
                }
            }
        }

        let listener: Listener = {
            target: target,
            cb: cb,
            once: once,
            priority: priority
        }

        if (insertIndex === -1) {
            list.push(listener);
        } else {
            list.splice(insertIndex, 0, listener);
        }
    }

    public once(event: string, cb: (...args: any[]) => void, target: Object): void {
        this.on(event, cb, target, true);
    }

    public off(event: string, cb: (...args: any[]) => void, target: Object): void {
        if (!cb || !target) {
            Logger.warn(`event: ${event} listener target 不能空`);
            return;
        }

        let map: Map<Object, Listener[]> = this._eventsMap.get(event);
        if (map === undefined) {
            Logger.warn(`event: ${event} 未注册该事件`);
            return;
        }

        let list: Listener[] = map.get(target);
        if (list === undefined) {
            Logger.warn(`event: ${event} target上未注册该事件`);
            return;
        }

        let length = list.length;

        // let index = list.findIndex((e) => { return e.cb === cb; });
        for (let i = 0; i < length; i++) {
            let bin = list[i];
            if (bin.cb === cb && bin.target === target) {
                list.splice(i, 1);
                if (list.length === 0) {
                    map.delete(target);
                    map.size === 0 && this._eventsMap.delete(event);
                }
                return;
            }
        }

        Logger.error(`event: ${event} target上未以该listener注册该事件`);
    }

    public emit(event: string, ...args: unknown[]): void {
        let map: Map<Object, Listener[]> = this._eventsMap.get(event);
        if (map === undefined) {
            Logger.warn((`event: ${event} 未注册该事件`));
            return;
        }

        let callArr: Array<{ cb: (...args: unknown[]) => void; target: Object }> = [];
        let onceArr: Array<{ cb: (...args: unknown[]) => void; target: Object }> = [];

        map.forEach((list, target) => {
            for (let index = 0; index < list.length; index++) {
                const element = list[index];
                callArr.push({ cb: element.cb, target: target });
                if (element.once) {
                    onceArr.push({ cb: element.cb, target: target });
                }
            }
        });

        for (let i = 0; i < onceArr.length; i++) {
            let e = onceArr[i];
            this.off(event, e.cb, e.target);
        }

        for (let i = 0; i < callArr.length; i++) {
            let e = callArr[i];
            e.cb.call(e.target, ...args);
            // e.cb.apply(e.target, args);
            // e.cb(...args);
        }
    }

    public async emitAsync(event: string, ...args: unknown[]): Promise<void> {
        let map: Map<Object, Listener[]> = this._eventsMap.get(event);
        if (map === undefined) {
            Logger.warn((`event: ${event} 未注册该事件`));
            return;
        }

        let callArr: Array<{ cb: (...args: unknown[]) => void; target: Object }> = [];
        let onceArr: Array<{ cb: (...args: unknown[]) => void; target: Object }> = [];

        map.forEach((list, target) => {
            for (let index = 0; index < list.length; index++) {
                const element = list[index];
                callArr.push({ cb: element.cb, target: target });
                if (element.once) {
                    onceArr.push({ cb: element.cb, target: target });
                }
            }
        });

        for (let i = 0; i < onceArr.length; i++) {
            let e = onceArr[i];
            this.off(event, e.cb, e.target);
        }

        let arr: Promise<unknown>[] = [];
        for (let i = 0; i < callArr.length; i++) {
            let e = callArr[i];
            arr.push(e.cb.apply(e.target, args));
        }

        await Promise.all(arr);
    }

    public registerEvents(constructor: any, event: string, funcName: string, once: boolean): void {
        let arr = this._classMap.get(constructor);
        if (arr === undefined) {
            arr = [];
            this._classMap.set(constructor, arr);
        } else {
            let find = arr.find((e) => {
                return e.event === event && e.funcName === funcName;
            });

            if (find) {
                Logger.error(`event: ${event} 重复注册`)
                return;
            }
        }

        arr.push({
            event: event,
            funcName: funcName,
            once: once
        });


    }

    public targetOn(target: Object, onSuper: boolean = true): void {
        if (onSuper) {
            this._classMap.forEach((value: EventData[], key: Function) => {
                if (target instanceof key) {
                    for (let i = 0; i < value.length; i++) {
                        let e = value[i];
                        this.on(e.event, target[e.funcName], target, e.once);
                    }
                }
            });
        } else {
            let arr = this._classMap.get(target.constructor);
            if (arr) {
                for (let i = 0; i < arr.length; i++) {
                    let e = arr[i];
                    this.on(e.event, target[e.funcName], target, e.once);
                }
            }
        }
    }

    public targetOff(target: Object): void {
        if (!target) {
            Logger.error(`event: ${target} target不能空`);
            return;
        }

        this._eventsMap.forEach((map, event) => {
            map.delete(target);
            map.size <= 0 && this._eventsMap.delete(event);
        })
    }


}